
#ifndef __APR_FUTURE_H
#define __APR_FUTURE_H

#include <vector>
#include <apr_atomic.h>
#include <apr_thread_cond.h>
#include <apr_portable.h>
#include "APRMutex.h"
#include "../iface/debug.h"
#include "../iface/SSLInterface.h"
#include "acquire_config.h"

/** A functor for generating the thread function in multiple
 * threads. */
typedef void *(*functor)(apr_thread_t *, void *);

/** A functor for generating the destructor (if elements in the
 * arguments need to be destroyed in the parent thread). */
typedef void *(*destructor)(std::vector<void *> *);

/** A template class for futures based on the APR thread structure. */
template <class T>
class APRFuture
{
   public:
      APRFuture(apr_pool_t *imp);
      ~APRFuture();
      void addArgument(void *arg);
      void addArgument(void *arg, apr_off_t length);
      void addArgument(const char *arg);
      void addArgument(const char *arg, apr_off_t length);
      void addArgument(int arg);
      void addArgument(apr_off_t arg);
      void addFunctor(functor i_functor);
      void addDestructor(destructor i_destructor);
      void makeThreadAttributes();
      void start();
      int cancel();
      static void *functorWrapper(apr_thread_t *tthread, void *args);
      const T *get() const;
      int hasStarted() const;
      int isFinished() const;
      /** Get the client handler as a function pointer. */
      virtual functor getFunctorWrapper() const { return (functor)(&functorWrapper); }
   private:
      std::vector<void *> arglist;
      std::vector<bool> to_be_freed_by_us;
      apr_thread_t *threadpntr;
      apr_threadattr_t *threadattrpntr;
      functor tfunctor;
      destructor tdestructor;
      volatile T *store;
      apr_pool_t *parent;
      apr_pool_t *mp;
      uint32_t started;
      uint32_t ready;
};

/** Constructor for the future and thread holder.
 * 
 * @param imp The parent memory pool. */
template <class T> APRFuture<T>::APRFuture(apr_pool_t *imp)
{
   apr_status_t status;
   parent = imp;
   status = apr_pool_create(&mp, imp);
   CheckAPRError(status);
   started = 0;
   ready = 0;
   addArgument((void *)this);
   threadpntr = NULL;
   threadattrpntr = NULL;
   store = NULL;
   tfunctor = NULL;
   tdestructor = NULL;
}

/** Destructor for the future and thread holder. */
template <class T> APRFuture<T>::~APRFuture()
{
   if (hasStarted())
   {
      apr_status_t status, thread_status;
      status = apr_thread_join(&thread_status, threadpntr);
      CheckAPRError(status);
   }
   if (tdestructor)
      tdestructor(&arglist);
   if (store) delete store;
   for (int i = 0; i < arglist.size(); i++)
      if (to_be_freed_by_us.at(i))
         free(arglist.at(i));
   apr_pool_destroy(mp);
}

/** Add an argument to the list of arguments for this thread.
 * 
 * @param arg Add a void pointer to the list of stuff to send to the
 * thread. */
template <class T> void APRFuture<T>::addArgument(void *arg)
{
   arglist.push_back(arg);
   to_be_freed_by_us.push_back(false);
}

/** Add an argument to the list of arguments for this thread.
 * 
 * @param arg Add a void pointer to the list of stuff to send to the
 * thread.
 * 
 * @param length The length of the buffer to use. */
template <class T> void APRFuture<T>::addArgument(void *arg, apr_off_t length)
{
   arglist.push_back(arg);
   to_be_freed_by_us.push_back(false);
   apr_off_t *newsizet = (apr_off_t *)malloc(sizeof(apr_off_t));
   *newsizet = length;
   arglist.push_back(newsizet);
   to_be_freed_by_us.push_back(true);
}

/** Add an argument to the list of arguments for this thread.
 * 
 * @param arg Add a const char pointer to the list of stuff to send to
 * the thread. */
template <class T> void APRFuture<T>::addArgument(const char *arg)
{
   arglist.push_back(const_cast<void *>((const void *)arg));
   to_be_freed_by_us.push_back(false);
}

/** Add an const char pointer arguments to the list of arguments for
 * this thread. Do this by using the length parameter to create a copy
 * of the buffer.
 * 
 * @param arg Add a const char pointer to the list of stuff to send to
 * the thread.
 * 
 * @param length The length of the buffer so that a copy might be
 * produced. */
template <class T> void APRFuture<T>::addArgument(const char *arg, apr_off_t length)
{
   char *newbuffer = (char *)malloc(sizeof(char)*(length+1));
   memcpy(newbuffer, arg, sizeof(char)*length);
   newbuffer[length] = '\0';
   arglist.push_back(newbuffer);
   to_be_freed_by_us.push_back(true);
}

/** Add an argument to the list of arguments for this thread.
 * 
 * @param arg Add a integer to the list of stuff to send to the
 * thread. */
template <class T> void APRFuture<T>::addArgument(int arg)
{
   int *newint = (int *)malloc(sizeof(int));
   *newint = arg;
   arglist.push_back(newint);
   to_be_freed_by_us.push_back(true);
}

/** Add an argument to the list of arguments for this thread.
 * 
 * @param arg Add a integer to the list of stuff to send to the
 * thread. */
template <class T> void APRFuture<T>::addArgument(apr_off_t arg)
{
   apr_off_t *newp = (apr_off_t *)malloc(sizeof(apr_off_t));
   *newp = arg;
   arglist.push_back(newp);
   to_be_freed_by_us.push_back(true);
}

/** Add in the functor function for the new thread to call.
 * 
 * @param i_functor Functor to add to the new thread. */
template <class T> void APRFuture<T>::addFunctor(functor i_functor)
{
   tfunctor = i_functor;
}

/** Add in the destructor function for the new thread to call.
 * 
 * @param i_destructor Destructor to add to the new thread. */
template <class T> void APRFuture<T>::addDestructor(destructor i_destructor)
{
   tdestructor = i_destructor;
}

/** Generate the thread attributes structure that can be edited to add
 * or remove thread characteristics. */
template <class T> void APRFuture<T>::makeThreadAttributes()
{
   apr_status_t status;
   status = apr_threadattr_create(&threadattrpntr, mp);
   CheckAPRError(status);   
}

/** A wrapper to set the future-specific things needed inside the
 * thread run.
 * 
 * @param tthread The APR thread used.
 * 
 * @param args The void pointer based thread arguments.
 * 
 * @return A void pointer of return 'data'. */
template <class T> void *APRFuture<T>::functorWrapper(apr_thread_t *tthread, void *args)
{
   APRFuture<T> *kthis_pntr = (APRFuture<T> *)((void **)args)[0];
   void *ret_pntr = (kthis_pntr->tfunctor)(tthread, &(((void **)args)[1]));
   // Cast returned pointer as (T *) and exchange it with NULL
#ifdef ATOMIC_EXCHANGE_POINTER
   void *previous = apr_atomic_xchgptr((volatile void **)(&(kthis_pntr->store)), (void *)ret_pntr);
#else
   void *previous = apr_atomic_casptr((volatile void **)(&(kthis_pntr->store)), (void *)ret_pntr, (void *)ret_pntr);
#endif
   apr_atomic_set32(&(kthis_pntr->ready), 1);
   apr_status_t status;
   ERR_remove_state(0);
   // Exit thread value is APR_SUCCESS (no error, the error should be
   // in the pointer-to-T)
   status = apr_thread_exit(tthread, APR_SUCCESS);
   CheckAPRError(status);
   return NULL;
}

/** Start the new thread. */
template <class T> void APRFuture<T>::start()
{
   if (tfunctor == NULL)
   {
      errprint("failed to set functor pointer");
      return;
   }
   if (threadattrpntr == NULL)
      makeThreadAttributes();
   if (started)
   {
      errprint("started previously: APR future cannot be restarted");
      return;
   }
   apr_status_t status;
   status = apr_thread_create(&threadpntr, threadattrpntr, getFunctorWrapper(), &(arglist.at(0)), mp);
   CheckAPRError(status);
   started = 1;
}

/** Cancel the running thread.
 * 
 * @return One if cancelled, zero if already finished/not started. */
template <class T> int APRFuture<T>::cancel()
{
   if ((!ready) && (started))
   {
      apr_status_t status;
      status = apr_thread_exit(threadpntr, APR_SUCCESS);
      CheckAPRError(status);
      return 1;
   }
   return 0;
}

/** Get the result from the thread who is held here.
 * 
 * @return Pointer to the templated object. */
template <class T> const T *APRFuture<T>::get() const
{
   while (!ready) apr_thread_yield();
   return (const T *)store;
}

/** Check if the task is started.
 * 
 * @return Non-zero if started, zero if not started. */
template <class T> int APRFuture<T>::hasStarted() const
{
   return started;
}

/** Check if the task is complete.
 * 
 * @return Non-zero if complete, zero if incomplete. */
template <class T> int APRFuture<T>::isFinished() const
{
   return ready;
}

#endif
